#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/select.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <dfs.h>
#include <errno.h>
#include "dfs_net.h"
#include "dfs_service.h"
#include "dfs_multies.h"

/* Function that accept client connections */
void client_accept_connection(int sd){
	extern fd_set master_list,client_list;
	extern int fdmax,client_fdmax;
	int remote_fd;
	struct sockaddr_in remoteaddr;
	socklen_t remote_addrlen;
	
	/* set remote address structure */
	remote_addrlen = sizeof(remoteaddr);
	remote_fd = accept(sd, (struct sockaddr *) &remoteaddr, &remote_addrlen);
	if(remote_fd < 0) perror("Error while accepting new connection");
	else {
		/* if no error occured put it in master list of fds */
		FD_SET(remote_fd, &master_list);
		if(remote_fd > fdmax) fdmax = remote_fd;
		
		/* also add in client list */
		FD_SET(remote_fd, &client_list);
		if(remote_fd > client_fdmax) client_fdmax = remote_fd;
		printf("[SERVICE][Server got new connection . Address %s . Socket %d]\n",inet_ntoa(remoteaddr.sin_addr),remote_fd);
	}
}

/* Function that handles connect_p2p message */
int handle_p2p_connection_msg(const char *data){
	extern fd_set master_list;
	extern int fdmax;
	char ipaddr[MAXIPADDRESSLEN];
	int sd,port,new_sd,items,r;
	struct sockaddr_in addr;
	struct hostent *he;

	/*Extracting address and port */
	memset(ipaddr,0,MAXIPADDRESSLEN);
	items = sscanf(data,"%s %d",ipaddr,&port);
	if(items < 2) return (ERROR);

	/* setting up sock and socket */
	memset(&addr,0,sizeof(addr));
	addr.sin_family  = AF_INET ;
	addr.sin_port = htons(port);
	he = gethostbyname(ipaddr);
	memcpy(&addr.sin_addr.s_addr,he->h_addr,he->h_length);	/* the minix way ! */
	/* create socket */
	sd = socket(addr.sin_family,SOCK_STREAM,0);
	if(sd < 0){
		perror("socket()");
		return (ERROR);
	}
	
	/* do connect with the peer */
	r=connect(sd, (struct sockaddr*)&addr, sizeof(addr));
	if(r < 0){
		/* FIXME remove from the above list */
		fprintf(stderr,"Cannot connect to %s\n",ipaddr);
		perror("connect()");
		return (ERROR);
	}
	
	/* add it to neighbours list */
	if (dfs_config_neighbours(ACT_CONNECTING,ipaddr,sd,NULL) != OK){ 
		/* failed adding to neighbours list */
		close(sd);
		printf("[P2P][Could not add %s to my neighbours]\n",ipaddr);
		return (ERROR);
	}
	
	/* if it is already added on the network list */
	if(FD_ISSET(sd,&master_list) || FD_ISSET(sd,&p2p_list)){
		close(sd);
		printf("[P2P][fd is already in master_list]\n");
		return (ERROR);
	}
	
	FD_SET(sd,&master_list); 
	if(new_sd > fdmax) fdmax = sd;
	FD_SET(sd,&p2p_list);
	if(new_sd > p2p_fdmax) p2p_fdmax = sd;
	printf("[P2P][Server connected to another peer .Address %s .Socket %d]\n",ipaddr,sd);
	return (OK);
}
/* Function that handles the sharing message */
int handle_sharing_connection_msg(const char *data){
	return dfs_config_files(ACT_INDEXING,data,NULL);
}
/* Function that handles the removal of a file from the server's sharing files */
int handle_remove_msg(const char *data){
	return dfs_config_files(ACT_REMOVING,data,NULL);
}
/* Function that handles listing of a file from the server's sharing files */
int handle_list_files_msg(char *data){
	return dfs_config_files(ACT_FILES,NULL,data);
}
/* Function that handles OPEN dfs request */
int handle_open_file_msg(const char *alias,int sd,char *filepath){
	int r;
	r = dfs_config_files(ACT_FILEPATH,alias,filepath); 
	if (r==OK) return OK; /* file in server's LFI */
	r = dfs_config_files(ACT_CACHEGET,alias,filepath);
	if (r==OK) return OK; /* file in cache */
	if(!FD_ISSET(sd,&p2p_list)){
		/* check net if (sd is in neighbours,exclude it)*/
		if(r==OK) return OK;
	}
	return ERROR;
}
